//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in August, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using MapWindow.Data;
using System.Drawing;
using MapWindow.Main.EventArguments;
using MapWindow.Geometries;
using System.ComponentModel;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


namespace MapWindow.Drawing
{
    /// <summary>
    /// This represents a point feature that also has drawing characteristics
    /// </summary>
    public class PointSymbolizer: FeatureSymbolizer, IPointSymbolizer
    {
        #region Variables

       
        private float _Angle;
        private IPointSymbolizer _DefaultPointProvider;
        private System.Drawing.Color _DotColor;
        private float _DotRadius;
        private TrueFalse _DotVisible;
        private System.Drawing.Icon _Icon;
        private System.Drawing.Image _Image;
        private PointShapes _Shape;
        private System.Drawing.SizeF _Size;
        private System.Drawing.Size _PixelSize;

       
       
        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new, empty instance of a PointFeatureSymbology
        /// </summary>
        public PointSymbolizer()
        {
            Clear();
            _Name = "New - No Parameters";
            _IsBusy = false;
            _PixelSize = new Size(24, 24);
        }

        /// <summary>
        /// As an example, a MapPointLayer has symbols that can act as the DefaultProvider
        /// in the event that one of the symbols is not defined.  If the symbol characteristic
        /// for an individual point isn't specified, it can check the Layer, but only if the
        /// layer is passed in as the default.  You could just as easilly define the 0th point
        /// to be a "master", or have a stand alone Symbolizer for this purpose.
        /// </summary>
        /// <param name="defaultProvider"></param>
        public PointSymbolizer(IPointSymbolizer defaultProvider) : base(defaultProvider)
        {
            _DefaultPointProvider = defaultProvider;
            Clear();
            _IsBusy = false;
        }

        /// <summary>
        /// This copies the shared characteristics from the specified IFeatureSymbolizer, but does
        /// not use the featureSymbolizer as a defaultProvider.  
        /// </summary>
        /// <param name="featureSymbolizer">Any valid Point, Line or Polygon Symbolizer that you want to copy 
        /// the properties from, so long as you are only interested in their shared, feature properties.</param>
        public PointSymbolizer(IFeatureSymbolizer featureSymbolizer)
        {
            Clear();
            Copy(featureSymbolizer);
            _IsBusy = false;
        }


        #endregion


        #region Methods

        /// <summary>
        /// This will clear the drawing characteristics of this MapPoint
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            _Angle = -999f; // Since an angle of 0 has meaning, -999 indicates to use the default
            _DotVisible = TrueFalse.False;
            _DotRadius = -1;
            _DotColor = Color.Empty;
            _Icon = null;
            _Image = null;
            _Shape = PointShapes.Empty;
            _Size = System.Drawing.SizeF.Empty;
            _BorderPen = null;
        }

        /// <summary>
        /// Copies all the point symbol properties from any valid implementation of IPointFeatureSymbology
        /// like a MapPoint or a MapPointLayer 
        /// </summary>
        /// <param name="copyFrom"></param>
        public void Copy(IPointSymbolizer copyFrom)
        {
            _Angle = copyFrom.Angle;

            // The pen can't be controlled by the property grid.
            // If it has valid characteristics, they should
            // match the sub-characteristics, so copy the sub
            // characteristics second.
            _BorderPen = copyFrom.BorderPen;
            BorderColor = copyFrom.BorderColor;
            BorderWidth = copyFrom.BorderWidth;
            
            if (copyFrom.BorderVisible == true)
            {
                _BorderVisible = TrueFalse.True;
            }
            else
            {
                _BorderVisible = TrueFalse.False;
            }
            

            _DotColor = copyFrom.DotColor;
            _DotRadius = copyFrom.DotRadius;
            if (copyFrom.DotVisible == true)
            {
                _DotVisible = TrueFalse.True;
            }
            else
            {
                _DotVisible = TrueFalse.False;
            }

            _FillBrush = copyFrom.FillBrush;
            _FillColor = copyFrom.FillColor;
            _Icon = copyFrom.Icon;
            _Image = copyFrom.Image;
            _Shape = copyFrom.Shape;
            _Size = copyFrom.Size;
        }

        

      
        

        /// <summary>
        /// This is a special case where we aren't interested in the normal positioning
        /// of the point, but we just want to draw the symbol for the
        /// point in the specified target location.  No changes will be made to the
        /// graphics object, however, so care should be taken that the transforms
        /// suit your needs before calling this.  Size will be more or less ignored.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        /// <param name="target">This specifies the pixel "Fit To" rectangle</param>
        /// <exception cref="ArgumentNullException">g cannot be null.</exception>
        /// <exception cref="ArgumentNullException">Target cannot be empty.</exception>
        public override void Draw(System.Drawing.Graphics g, Rectangle clipRectangle, Rectangle target)
        {
            _IsBusy = true;
            System.Drawing.Drawing2D.Matrix oldMatrix = null;

            try
            {


                if (g == null)
                {
                    OnPaintCancelled(new ArgumentNullException("g cannot be null."), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                oldMatrix = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
                System.Drawing.Drawing2D.Matrix mat = g.Transform;
                // If the point is not visible, return
                if (Visible == false)
                {
                    OnPaintCancelled(new OperationCanceledException("MapPoint not Visible"), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                // return OnBeforePainting 
                if (OnBeforePainting(g, clipRectangle, target) == true) // Drawing Canceled
                {

                    OnPaintCancelled(new OperationCanceledException("Canceled by OnBeforePainting"), g, clipRectangle, target);
                    return;
                }

                // Usually clipping is done at the MapFrame level, but sometimes we are drawing an image somewhere
                if (clipRectangle.IsEmpty == false)
                {
                    // use the intersection of any clipping that has already been applied and the specified rectangle
                    g.IntersectClip(clipRectangle);
                }


                // Pens can't have separate values in x and y
                float minScale = 1;
                float xScale = 1;
                float yScale = 1;

                // These variables control features that need to be re-calculated depending on the drawing mode
                System.Drawing.Pen ScaledBorderPen = BorderPen;
                float ScaledDotRadius = DotRadius;
                SizeF ScaledSize = this.Size;

                // Create a Transform if a target exists, otherwise, just use the existing transform
                if (target.IsEmpty == false)
                {
                    // Before applying any transforms, modify the clip region in the previous transform environment
                    //g.IntersectClip(target);

                    // Initialize a new transform that will scale the image to the target
                    mat = new System.Drawing.Drawing2D.Matrix();

                    // While we are in pixel coordinates, translate to the specified Frame
                    mat.Translate((float)target.X, (float)target.Y);

                    // Scaling 
                    // An envelope width or height of 0 will throw an exception.
                    // I think that is better than simply not drawing, since it helps track the problem
                    xScale = (float)(target.Width / _Size.Width);
                    yScale = (float)(target.Height / _Size.Height);
                    minScale = Math.Min(xScale, yScale);

                    // Don't make this negative in y.  When people are specifying a target, they
                    // are expecting the point to be drawn right-side up in the target.
                    mat.Scale(xScale, yScale);

                    // I control the transform this way because I can't seem to apply methods to the 
                    // graphics transform directly.
                    g.Transform = mat;

                }
                else
                {
                    // Draw a pixel the same size regardless of the zoom level if ScaleMode = Pixels
                    if (ScaleMode == ScaleModes.Pixels)
                    {
                        // Assume the matrix was sent in in world coordintes, but they want us to draw in pixel sizes
                        xScale = (float)(g.Transform.Elements[0]);
                        yScale = (float)(g.Transform.Elements[3]);
                        minScale = Math.Min(Math.Abs(xScale), Math.Abs(yScale));

                        // By adjusting these here, but not in the case where there is a Frame, we can let the 
                        // Image components keep the same proportions
                        ScaledBorderPen = (Pen)BorderPen.Clone(); // Clone this so we don't mess up the original
                        ScaledBorderPen.Width = ScaledBorderPen.Width / minScale;
                        ScaledDotRadius = ScaledDotRadius / minScale;
                        ScaledSize = new SizeF(ScaledSize.Width / xScale, ScaledSize.Height / yScale);
                    }
                    // otherwise, this simply will allow us to draw in feet or meters or whatever for cases where
                    // the user wants to draw a 20m x 20m square somewhere.


                }


                // Anti-aliasing makes things much smoother, but could spell a performance hit?
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                // Set up the new drawing space so that it is centered on the 0,0 instead of using it as the top-left
                mat = g.Transform;
                mat.Translate(ScaledSize.Width / 2, ScaledSize.Height / 2);
                mat.Rotate(Angle); // rotate the transform so that no matter what we draw, it will be rotated correctly
                g.Transform = mat;
                if (BorderVisible == false)
                {
                    ScaledBorderPen = null;
                }
                RectangleF rectScaled = new RectangleF(-ScaledSize.Width / 2, -ScaledSize.Height / 2, ScaledSize.Width, ScaledSize.Height);
                string temp = _Name;


                // TIME TESTING

               

                    // END TIME TESTING

                    switch (Shape)
                    {
                        case PointShapes.Diamond:
                            DrawRegularPoly(g, ScaledBorderPen, FillBrush, ScaledSize, 4);
                            break;
                        case PointShapes.Ellipse:
                            DrawEllipse(g, ScaledBorderPen, FillBrush, ScaledSize);
                            break;
                        case PointShapes.Icon:
                            Rectangle intRect;

                            // Oddly, I can't specify a size in float coordinates, but I can tweak the size
                            // by making use of the g.Transform
                            if (ScaleMode == ScaleModes.Pixels)
                            {
                                // Undo the scaling transform and use the regular size
                                mat.Scale(1 / xScale, 1 / yScale);
                                g.Transform = mat;
                                intRect = new Rectangle(-(int)Size.Width / 2, -(int)Size.Height / 2, (int)Size.Width, (int)Size.Height);
                                g.DrawIcon(Icon, intRect);
                            }
                            else
                            {
                                mat.Scale(rectScaled.Width / (float)((int)rectScaled.Width), rectScaled.Height / (float)((int)rectScaled.Height));
                                g.Transform = mat;
                                intRect = new Rectangle((int)rectScaled.X, (int)rectScaled.Y, (int)rectScaled.Width, (int)rectScaled.Height);
                                g.DrawIcon(Icon, intRect);
                            }
                            g.DrawIcon(Icon, intRect);
                            break;
                        case PointShapes.Image:
                            if (Image != null)
                            {
                                g.DrawImage(Image, rectScaled);
                            }
                            break;
                        case PointShapes.Hexagon:
                            DrawRegularPoly(g, ScaledBorderPen, FillBrush, ScaledSize, 6);
                            break;
                        case PointShapes.Pentagon:
                            DrawRegularPoly(g, ScaledBorderPen, FillBrush, ScaledSize, 5);
                            break;
                        case PointShapes.Rectangle:
                            g.FillRectangle(FillBrush, rectScaled);
                            g.DrawRectangle(ScaledBorderPen, rectScaled.X, rectScaled.Y, rectScaled.Width, rectScaled.Height);
                            break;
                        case PointShapes.Star:
                            DrawStar(g, ScaledBorderPen, FillBrush, ScaledSize);
                            break;
                        case PointShapes.Triangle:
                            DrawRegularPoly(g, ScaledBorderPen, FillBrush, ScaledSize, 3);
                            break;
                        default:
                            break;
                    }

                    if (DotVisible)
                    {
                        float r = ScaledDotRadius;
                        g.FillEllipse(new SolidBrush(DotColor), new RectangleF(-r, -r, r * 2, r * 2));
                    }
                // TIme testing
               

                // End time testing
            }
            catch (Exception ex)
            {
                OnPaintCancelled(ex, g, clipRectangle, target);

            }
            finally
            {
                if (oldMatrix != null) g.Transform = oldMatrix;

            }
        }

        /// <summary>
        /// This is designed to ignore geospatial handling and simply draw the specified symbol using everthing
        /// as a pixel coordinate.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <exception cref="ArgumentNullException">g cannot be null.</exception>
        public override void Draw(System.Drawing.Graphics g)
        {
            System.Drawing.Drawing2D.Matrix oldMatrix = null;

            try
            {


                if (g == null)
                {
                    OnPaintCancelled(new ArgumentNullException("g cannot be null."), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                oldMatrix = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
                System.Drawing.Drawing2D.Matrix mat = g.Transform;
                // If the point is not visible, return
                if (Visible == false)
                {
                    OnPaintCancelled(new OperationCanceledException("MapPoint not Visible"), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                // return OnBeforePainting 
                if (OnBeforePainting(g, Rectangle.Empty, Rectangle.Empty) == true) // Drawing Canceled
                {

                    OnPaintCancelled(new OperationCanceledException("Canceled by OnBeforePainting"), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

       
                // These variables control features that need to be re-calculated depending on the drawing mode
               
                // Anti-aliasing makes things much smoother, but could spell a performance hit?
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                
                mat = g.Transform;
                mat.Translate(_PixelSize.Width / 2 -1, _PixelSize.Height / 2 -1);
                mat.Rotate(Angle); // rotate the transform so that no matter what we draw, it will be rotated correctly
                g.Transform = mat;
                Pen scaledBorderPen = BorderPen;
                if(BorderVisible == false)
                {
                    scaledBorderPen = null;
                }
                RectangleF rectScaled = new RectangleF(1, 1, _PixelSize.Width-2, _PixelSize.Height-2);
                SizeF sizeScaled = new SizeF(_PixelSize.Width - 2, _PixelSize.Height - 2);
                switch (Shape)
                {
                    case PointShapes.Diamond:
                        DrawRegularPoly(g, scaledBorderPen, FillBrush, sizeScaled, 4);
                        break;
                    case PointShapes.Ellipse:
                        DrawEllipse(g, scaledBorderPen, FillBrush, sizeScaled);
                        break;
                    case PointShapes.Icon:
                        if (Icon != null)
                        {
                            g.DrawIcon(Icon, 0, 0);
                        }
                        break;
                    case PointShapes.Image:
                        if (Image != null)
                        {
                            g.DrawImage(Image, rectScaled);
                        }
                        break;
                    case PointShapes.Hexagon:
                        DrawRegularPoly(g, scaledBorderPen, FillBrush, sizeScaled, 6);
                        break;
                    case PointShapes.Pentagon:
                        DrawRegularPoly(g, scaledBorderPen, FillBrush, sizeScaled, 5);
                        break;
                    case PointShapes.Rectangle:
                     
                        g.FillRectangle(FillBrush, rectScaled);
                        g.DrawRectangle(scaledBorderPen, 1, 1, _PixelSize.Width - 2, _PixelSize.Height - 2);
                        break;
                    case PointShapes.Star:
                        DrawStar(g, scaledBorderPen, FillBrush, sizeScaled);
                        break;
                    case PointShapes.Triangle:
                        DrawRegularPoly(g, scaledBorderPen, FillBrush, sizeScaled, 3);
                        break;
                    default:
                        break;
                }

                if (DotVisible)
                {
                    float r = DotRadius;
                    g.FillEllipse(new SolidBrush(DotColor), new RectangleF(-r, -r, r * 2, r * 2));
                }
                // TIme testing


                // End time testing
            }
            catch (Exception ex)
            {
                OnPaintCancelled(ex, g, Rectangle.Empty, Rectangle.Empty);

            }
            finally
            {
                if (oldMatrix != null) g.Transform = oldMatrix;

            }
        }


        /// <summary>
        /// Creates an image using the symbol properties and returns an appropriate texture.
        /// </summary>
        /// <param name="device">The Microsoft.DirectX.Direct3D device for controlling texture creation</param>
        /// <returns>A Texture that should represent the symbol for this icon</returns>
        public Texture CreateTexture(Device device)
        {
            // Manufacture a point sprite
            Bitmap bmp = new Bitmap(_PixelSize.Width, _PixelSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graphics g = Graphics.FromImage(bmp);
            g.FillRectangle(new SolidBrush(Color.FromArgb(0, 0, 0, 0)), new Rectangle(0, 0, _PixelSize.Width -1 , _PixelSize.Height - 1));
            this.Draw(g);
            g.Dispose();
            return new Texture(device, bmp, Usage.SoftwareProcessing, Pool.Managed);
        }

      
        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets the float angle, measured counter-clockwise from horizontal - east
        /// for this point.  Setting this to something outside -360 to 360 will use the layer default.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets the float angle, measured counter-clockwise from horizontal - east")]
        public float Angle
        {
            get
            {
                if (_Angle < -360f || _Angle > 360f)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.Angle; // Use default
                    }
                    return 0;
                }
                return _Angle;
            }
            set
            {
                _Angle = value;
                _IsEmpty = false;
            }
        }


        /// <summary>
        /// Gets or sets the color for a dot that will be placed at the center of the symbol.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets the color for a dot that will be placed at the center of the symbol.")]
        public System.Drawing.Color DotColor
        {
            get
            {
                if (_DotColor == Color.Empty)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.DotColor;
                    }
                }
                return _DotColor;
            }
            set
            {
                _DotColor = Color.FromArgb(Convert.ToByte(255 * _Opacity), value.R, value.G, value.B);
                _DotColor = value;
                _IsEmpty = false;
            }
        }

        /// <summary>
        /// Gets or sets the float radius for the dot at the center of the symbol
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets the float radius for the dot at the center of the symbol.")]
        public float DotRadius
        {
            get
            {
                if (_DotRadius < 0)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.DotRadius;
                    }
                }
                return _DotRadius;
            }
            set
            {
                _DotRadius = value;
                _IsEmpty = false;
            }
        }

        /// <summary>
        /// Gets or sets a boolean value indicating whether or not the dot will be visible for this symbol.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute(" Gets or sets a boolean value indicating whether or not the dot will be visible for this symbol.")]
        public bool DotVisible
        {
            get
            {
                if (_DotVisible == TrueFalse.Empty)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.DotVisible;
                    }
                    return false;
                }
                if (_DotVisible == TrueFalse.True)
                {
                    return true;
                }
                return false;
            }
            set
            {
                if (value == true)
                {
                    _DotVisible = TrueFalse.True;
                    _IsEmpty = false;
                }
                else
                {
                    _DotVisible = TrueFalse.False;
                    _IsEmpty = false;
                }
            }
        }

       

        /// <summary>
        /// Gets or sets a true-size icon to be used if the Shape property
        /// is set to Icon.  This will draw the icon
        /// without changing it to fit the point size or target.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets a true-size icon to be used if the Shape property is set to Icon.  This will draw the icon without changing it to fit the point size or target.")]
        public System.Drawing.Icon Icon
        {
            get
            {
                if (_Icon == null)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.Icon;
                    }
                }
                return _Icon;
            }
            set
            {
                _Icon = value;
                _IsEmpty = false;
            }
        }

        /// <summary>
        /// Gets or sets a System.Drawing.Image to draw in the case
        /// where the Shape is set to Image.  This will draw a scaled version 
        /// of the image specified.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets a System.Drawing.Image to draw if the Shape is set to Image.  This will draw a scaled version of the image specified.")]
        public System.Drawing.Image Image
        {
            get
            {
                if (_Image == null)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.Image;
                    }
                }
                return _Image;
            }
            set
            {
                _Image = value;
                _IsEmpty = false;
            }
        }

        /// <summary>
        /// Gets or sets a float value from 0 to 1 where 0 is completely transparent
        /// and 1 is completely solid.  Setting an alpha of a specific feature, like
        /// FillColor, to something less than 255 will control that feature's transparency
        /// without affecting the others.  The final transparency of the feature will
        /// be that alpha multiplied by this value and rounded to the nearest byte.
        /// </summary>
        public override float Opacity
        {
            get 
            {
                return base.Opacity;
            }
            set
            {
                base.Opacity = value;

                float val = value;
                if (val > 1) val = 1;
                if (val < 0) val = 0;
                byte A = Convert.ToByte(255 * val);

                // In addition to controlling Border color and fill color, we also control the dot color
                DotColor = Color.FromArgb(A, DotColor.R, DotColor.G, DotColor.B);
            }
        }

         

        /// <summary>
        /// Before drawing to the real world, a sprite is created and used as a texture.
        /// This controls the number of pixels that are on the texture, which will be
        /// stretched in order to fit the real world dimensions.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets a System.Drawing.Size that represents the number of pixels that the underlying texture will use.  This won't affect the size in geographic space.")]
        public System.Drawing.Size PixelSize
        {
            get { return _PixelSize; }
            set { _PixelSize = value; }
        }
        

        /// <summary>
        /// A MapWindow.Drawing.PointShapes enumeration to clarify what kind of points
        /// to draw for the image.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Clarifies a point shape or specifies the use of an image what kind of points to draw.")]
        public PointShapes Shape
        {
            get
            {
                if (_Shape == PointShapes.Empty)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.Shape;
                    }
                }
                return _Shape;
            }
            set
            {
                _Shape = value;
                _IsEmpty = false;
            }
        }

        /// <summary>
        /// Gets or sets the System.Drawing.SizeF in pixels to make this MapPoint.
        /// </summary>
        [CategoryAttribute("Appearance"), DescriptionAttribute("Gets or sets the System.Drawing.SizeF in pixels to make this MapPoint.")]
        public System.Drawing.SizeF Size
        {
            get
            {
                if (_Size == SizeF.Empty)
                {
                    if (_DefaultPointProvider != null)
                    {
                        return _DefaultPointProvider.Size;
                    }
                }
                return _Size;
            }
            set
            {
                _Size = value;
                _IsEmpty = false;
            }
        }

        

       
        #endregion


        

        // Some methods here like DrawStar and DrawRegular polygon might be useful tools in general, so I made them static.

        #region Static Methods

        /// <summary>
        /// Draws an ellipse on the specified graphics surface.
        /// </summary>
        /// <param name="g">The graphics surface to draw on</param>
        /// <param name="ScaledBorderPen">The Pen to use for the border, or null if no border should be drawn</param>
        /// <param name="fillBrush">The Brush to use for the fill, or null if no fill should be drawn</param>
        /// <param name="ScaledSize">The size to fit the ellipse into (the ellipse will be centered at 0,0)</param>
        public static void DrawEllipse(Graphics g, Pen ScaledBorderPen, Brush fillBrush, SizeF ScaledSize)
        {
            PointF UpperLeft = new PointF(-ScaledSize.Width / 2, -ScaledSize.Height / 2);
            RectangleF destRect = new RectangleF(UpperLeft, ScaledSize);
            if (fillBrush != null)
            {
                g.FillEllipse(fillBrush, destRect);
            }
            if (ScaledBorderPen != null)
            {
                g.DrawEllipse(ScaledBorderPen, destRect);
            }
        }

       
        /// <summary>
        /// Draws a regular polygon with equal sides.  The first point will be located all the way to the right on the X axis.
        /// </summary>
        /// <param name="g">Specifies the System.Drawing.Graphics surface to draw on</param>
        /// <param name="ScaledBorderPen">Specifies the System.Drawing.Pen to use for the border</param>
        /// <param name="fillBrush">Specifies the System.Drawing.Brush to use for to fill the shape</param>
        /// <param name="ScaledSize">Specifies the System.Drawing.SizeF to fit the polygon into</param>
        /// <param name="NumSides">Specifies the integer number of sides that the polygon should have</param>
        public static void DrawRegularPoly(Graphics g, Pen ScaledBorderPen, Brush fillBrush, SizeF ScaledSize, int NumSides)
        {
            PointF[] PolyPoints;
            PolyPoints = new PointF[NumSides + 1]; // One extra point to close the polygon

            // Instead of figuring out the points in cartesian, figure them out in angles and re-convert them.
            for (int I = 0; I <= NumSides; I++)
            {
                double ang = I * (2 * Math.PI) / NumSides;
                float x = System.Convert.ToSingle(Math.Cos(ang)) * ScaledSize.Width / 2f;
                float y = System.Convert.ToSingle(Math.Sin(ang)) * ScaledSize.Height / 2f;
                PolyPoints[I] = new PointF(x, y);
            }
            if (fillBrush != null)
            {
                g.FillPolygon(fillBrush, PolyPoints, System.Drawing.Drawing2D.FillMode.Alternate);
            }
            if (ScaledBorderPen != null)
            {
                g.DrawPolygon(ScaledBorderPen, PolyPoints);
            }

        }

        /// <summary>
        /// Draws a 5 pointed star with the points having twice the radius as the bends.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on</param>
        /// <param name="ScaledBorderPen">The System.Drawing.Pen to draw the border with</param>
        /// <param name="fillBrush">The System.Drawing.Brush to use to fill the Star</param>
        /// <param name="ScaledSize">The System.Drawing.SizeF size to fit the Start to</param>
        public static void DrawStar(Graphics g, Pen ScaledBorderPen, Brush fillBrush, SizeF ScaledSize)
        {

            PointF[] PolyPoints = new PointF[11];
            for (int I = 0; I <= 10; I++)
            {
                double ang = I * Math.PI / 5;

                float x = System.Convert.ToSingle(Math.Cos(ang)) * ScaledSize.Width / 2f;
                float y = System.Convert.ToSingle(Math.Sin(ang)) * ScaledSize.Height / 2f;
                if (I % 2 == 0)
                {
                    x /= 2; // the shorter radius points of the star
                    y /= 2;
                }
                PolyPoints[I] = new PointF(x, y);
            }
            if (fillBrush != null)
            {
                g.FillPolygon(fillBrush, PolyPoints, System.Drawing.Drawing2D.FillMode.Alternate);
            }
            if (ScaledBorderPen != null)
            {
                g.DrawPolygon(ScaledBorderPen, PolyPoints);
            }

        }

        #endregion
    }
}
